/**
 * D5Power Studio FPower 2D MMORPG Engine
 * 第五动力FPower 2D 多人在线角色扮演类网页游戏引擎
 * 
 * copyright [c] 2010 by D5Power.com Allrights Reserved.
 */ 
package com.D5Power.Controler
{
	import com.D5Power.GMath.GMath;
	import com.D5Power.Objects.CharacterObject;
	import com.D5Power.Objects.Directions;
	import com.D5Power.Objects.GameObject;
	import com.D5Power.core.SilzAstar;
	import com.D5Power.core.Vector2D;
	import com.D5Power.map.WorldMap;
	import com.D5Power.utils.XYArray;
	
	import flash.display.Stage;
	import flash.events.KeyboardEvent;
	import flash.events.MouseEvent;
	import flash.geom.Point;
	

	/**
	 * 角色控制器
	 * 
	 */ 
	public class CharacterControler extends BaseControler
	{
		private var target:Point;
		
		private var onControl:Boolean=false;
		
		private var _step:uint;
		
		private var _path:Array;
		
		protected var contrlSwitch:uint;
		
		public static const KEY:uint=1;
		public static const KEY_AND_MOUSE:uint=2;
		public static const MOUSE:uint=0;
		
		/**
		 * 各按键按下状态
		 */ 
		protected var wKey:int;
		protected var aKey:int;
		protected var sKey:int;
		protected var dKey:int;
		/**
		 * 根据按键产生的XY轴行走方向
		 */ 
		protected var xKey:int;
		protected var yKey:int;
		
		protected var _k:Number = Math.sin(Math.PI*45/180);
		
		protected var _listnerSetuped:Boolean=false;

		public function CharacterControler(pec:Perception,ctrl:uint = KEY_AND_MOUSE)
		{
			super(pec);

			_step = 1;
			contrlSwitch = ctrl;
			
			
			setupListener();
		}
		
		/**
		 * 根据控制方式的不同设置不同的侦听
		 */ 
		override public function setupListener():void
		{
			if(_listnerSetuped) return;
			
			var s:Stage = perception.Scene.stage;
			switch(contrlSwitch)
			{
				case KEY:
					s.addEventListener(KeyboardEvent.KEY_DOWN,onKeyDown,false,0,true);
					s.addEventListener(KeyboardEvent.KEY_UP,onKeyUp,false,0,true);
					s.addEventListener(MouseEvent.CLICK,onDo,false,0,true);
					break;
				case MOUSE:
					s.addEventListener(MouseEvent.CLICK,onClick,false,0,true);
					break;
				case KEY_AND_MOUSE:
					s.addEventListener(MouseEvent.MOUSE_MOVE,onMouseMove);
					s.addEventListener(KeyboardEvent.KEY_DOWN,onKeyDown,false,0,true);
					s.addEventListener(KeyboardEvent.KEY_UP,onKeyUp,false,0,true);
					s.addEventListener(MouseEvent.CLICK,onDo,false,0,true);
					break;
			}
			_listnerSetuped = true;
		}
		
		/**
		 * 反安装控制器
		 */ 
		override public function unsetupListener():void
		{
			if(!_listnerSetuped) return;
			
			var s:Stage = perception.Scene.stage;
			
			switch(contrlSwitch)
			{
				case KEY:
					s.removeEventListener(KeyboardEvent.KEY_DOWN,onKeyDown);
					s.removeEventListener(KeyboardEvent.KEY_UP,onKeyUp);
					s.removeEventListener(MouseEvent.CLICK,onDo);
					break;
				case MOUSE:
					s.removeEventListener(MouseEvent.CLICK,onClick);
					break;
				case KEY_AND_MOUSE:
					s.removeEventListener(MouseEvent.MOUSE_MOVE,onMouseMove);
					s.removeEventListener(KeyboardEvent.KEY_DOWN,onKeyDown);
					s.removeEventListener(KeyboardEvent.KEY_UP,onKeyUp);
					s.removeEventListener(MouseEvent.CLICK,onDo);
					break;
			}
			_listnerSetuped = false;
		}
		
		/**
		 * 计算行动
		 */ 
		override public function calcAction():void
		{
			switch(contrlSwitch)
			{
				case KEY:
					calcActionKey();
					break;
				case MOUSE:
					calcActionMouse();
					break;
				case KEY_AND_MOUSE:
					calcActionBoth();
					break;
			}
		}
		
		/**
		 * 触发鼠标动作
		 */ 
		protected function onDo(e:MouseEvent):void
		{
			
		}
		
		/**
		 * 是否静止
		 * 处于死亡状态返回是
		 */ 
		protected function get isStatic():Boolean
		{
			if(perception.me==null) return false;
			if((perception.me as CharacterObject).action==Actions.Die) return true;
			
			return false;
		}
		
		/**
		 * 全控制状态下的动作计算
		 */ 
		protected function calcActionBoth():void
		{
			if(isStatic) return;
			
			var me:CharacterObject = perception.me as CharacterObject;
			var nextX:Number=me.Pos.x;
			var nextY:Number=me.Pos.y;

			/**
			 * 根据按键移动角色方向
			 */ 
			switch(xKey)
			{
				case 1:
					if(yKey==1)
					{
						nextX+=me.speed*_k;
						nextY+=me.speed*_k;
					}else if(yKey==-1){
						nextX+=me.speed*_k;
						nextY-=me.speed*_k;
					}else{
						nextX=me.Pos.x+me.speed;
					}
					break;
				case -1:
					if(yKey==1)
					{
						nextX-=me.speed*_k;
						nextY+=me.speed*_k;
					}else if(yKey==-1){
						nextX-=me.speed*_k;
						nextY-=me.speed*_k;
					}else{
						nextX=me.Pos.x-me.speed;
					}
					break;
				case 0:
					if(yKey==1)
					{
						nextY=me.Pos.y+me.speed;
					}else if(yKey==-1){
						nextY=me.Pos.y-me.speed;
					}else{
						if(me.action==Actions.Run) me.action = Actions.Stop;
						return;
					}
					break;
			}
			nextCanMove(nextX,nextY,me);
		}
		
		/**
		 * 鼠标控制状态下的计算行动
		 */ 
		protected function calcActionMouse():void
		{
			if(isStatic) return;
			
			var c:CharacterObject = perception.me as CharacterObject;
			
			if(_path!=null && _path[_step]!=null)
			{
				if(c.action==Actions.Stop) c.action = Actions.Run;
				
				_nextTarget = _step==_path.length ? _endTarget : WorldMap.tile2WorldPostion(_path[_step][0],_path[_step][1]);
				
				
				var radian:Number = GMath.getPointAngle(_nextTarget.x-c.Pos.x,_nextTarget.y-c.Pos.y);
				var angle:int = GMath.R2A(radian)+90;
				
				var xisok:Boolean=false;
				var yisok:Boolean=false;
				
				var xspeed:Number = c.speed*Math.cos(radian);
				var yspeed:Number = c.speed*Math.sin(radian);
				
				
				if(Math.abs(c.Pos.x-_nextTarget.x)<=xspeed)
				{
					xisok=true;
					xspeed=0;
				}
				
				if(Math.abs(c.Pos.y-_nextTarget.y)<=yspeed)
				{
					yisok=true;
					yspeed=0;
				}
				
				c.Pos=new Point(c.Pos.x+xspeed,c.Pos.y+yspeed);
				
				if(xisok && yisok)
				{
					// 走到新的位置点 更新区块坐标
					c._tileX = _path[_step][0];
					c._tileY = _path[_step][1];
					
					_step++;
					if(_step>=_path.length)
					{
						c.action = Actions.Stop;
						_path=null;
						_step=1;
					}
				}else{
					changeDirectionByAngle(angle);
				}
			}
		}
		
		/**
		 * 键盘控制状态下的动作计算
		 */ 
		protected function calcActionKey():void
		{
			if(isStatic) return;
			
			var me:CharacterObject = perception.me as CharacterObject;
			var nextX:Number=me.Pos.x;
			var nextY:Number=me.Pos.y;
			
			
			/**
			 * 根据按键状态修改角色朝向，并修改移动方向
			 */ 
			switch(xKey)
			{
				case 1:
					if(yKey==1)
					{
						me.directionNum = Directions.RightDown;
						nextX+=me.speed*_k;
						nextY+=me.speed*_k;
					}else if(yKey==-1){
						me.directionNum = Directions.RightUp;
						nextX+=me.speed*_k;
						nextY-=me.speed*_k;
					}else{
						me.directionNum = Directions.Right;
						nextX=me.Pos.x+me.speed;
					}
					break;
				case -1:
					if(yKey==1)
					{
						me.directionNum = Directions.LeftDown;
						nextX-=me.speed*_k;
						nextY+=me.speed*_k;
					}else if(yKey==-1){
						me.directionNum = Directions.LeftUp;
						nextX-=me.speed*_k;
						nextY-=me.speed*_k;
					}else{
						me.directionNum = Directions.Left;
						nextX=me.Pos.x-me.speed;
					}
					break;
				case 0:
					if(yKey==1)
					{
						me.directionNum = Directions.Down;
						nextY=me.Pos.y+me.speed;
					}else if(yKey==-1){
						nextY=me.Pos.y-me.speed;
						me.directionNum = Directions.Up;
					}else{
						if(me.action==Actions.Run) me.action = Actions.Stop;
						return;
					}
					break;
			}
			
			nextCanMove(nextX,nextY,me);
		}
		
		/**
		 * 下一目标点是否可以移动
		 * for 键盘控制
		 */ 
		protected function nextCanMove(nextX:Number,nextY:Number,me:CharacterObject):void
		{
			// 不可移出屏幕
			if(nextX>Global.MAPSIZE.x || nextX<0) return;
			if(nextY>Global.MAPSIZE.y || nextY<0) return;
			
			// 不可移动到地图非0区域
			var p:XYArray = WorldMap.Postion2Tile(new Point(nextX,nextY));
			if(perception.Scene.Map.roadMap[p.y][p.x]!=0) return;
			
			moveTo(nextX,nextY,me);
		}
		/**
		 * 移动主角
		 */ 
		protected function moveTo(nextX:Number,nextY:Number,me:CharacterObject):void
		{
			me.Pos = new Point(nextX,nextY);
			if(me.action!=Actions.Attack)me.action = Actions.Run;
		}
		
		
		
		
		/**
		 * 通知服务器已发生了移动
		 * @param	p	移动坐标
		 */ 
		protected function tellServerMove(p:Point):void
		{
			// do some thing
		}
		
		/**
		 * 点击到了某对象
		 */ 
		protected function clickSomeBody(o:GameObject):void
		{
			// do some thime
		}
		
		/**
		 * 鼠标移动侦听，使角色始终面向鼠标
		 */ 
		public function onMouseMove(e:MouseEvent):void
		{
			if(isStatic) return;
			
			if(perception.me==null) return;
			var p:Point = perception.Scene.Map.getScreenPostion(perception.me.Pos.x,perception.me.Pos.y);
			var radian:Number = GMath.getPointAngle(e.stageX-p.x,e.stageY-p.y);
			var angle:int = GMath.R2A(radian)+90;
			changeDirectionByAngle(angle);
			
			if(angle>=0 && angle<=180 && xKey==-1)
			{
				// 向右移动，如果键盘方向为左，则倒行
				(perception.me as CharacterObject).isBackWalk=true;
				return;
			}
			
			if((angle>180 || angle<0) && xKey==1)
			{
				// 向左移动，如果键盘方向为右，则倒行
				(perception.me as CharacterObject).isBackWalk=true;
				return;
			}
			
			if(angle>=-90 && angle<=90 && yKey==1)
			{
				// 向上移动，如果键盘方向为下，则倒行
				(perception.me as CharacterObject).isBackWalk=true;
				return;
			}
			
			if(angle>90 && angle<180 && yKey==-1)
			{
				// 向下移动，如果键盘方向为上，则倒行
				(perception.me as CharacterObject).isBackWalk=true;
				return;
			}
			
			(perception.me as CharacterObject).isBackWalk=false;
		}
		/**
		 * 鼠标响应驱动
		 */ 
		private function onClick(e:MouseEvent):void
		{
			if(isStatic) return;
			
			// 检查是否点到某对象
			var clicker:GameObject = perception.getClicker(new Point(e.stageX,e.stageY));
			if(clicker!=null)
			{
				clickSomeBody(clicker);
				return;
			}
			
			var me:CharacterObject = perception.me as CharacterObject;
			var m:WorldMap = perception.Scene.Map;

			// 计算世界坐标
			_endTarget = m.getWorldPostion(e.stageX,e.stageY);

			// 计算格子数
			var tileX:uint = int(_endTarget.x/WorldMap.tileWidth);
			var tileY:uint = int(_endTarget.y/WorldMap.tileHeight);

			var startX:uint = int(me.Pos.x/WorldMap.tileWidth);
			var startY:uint = int(me.Pos.y/WorldMap.tileHeight);
						
			me._tileX = startX;
			me._tileY = startY;
			me.action = Actions.Stop;

			// 得出路径
			var nodeArr:Array = WorldMap.AStar.find(me.Pos.x,me.Pos.y,_endTarget.x,_endTarget.y);
			if(nodeArr==null)
			{
				return;
			}else{
				_path = new Array();
				var i:uint;
				for(i=0;i<nodeArr.length;i++)
				{
					_path.push([nodeArr[i].x,nodeArr[i].y]);
				}
			}
			_step=1;

			// 向服务器发送同步数据
			tellServerMove(_endTarget);
			
		}
		
		/**
		 * 重置按键状态
		 */ 
		protected function resetKeyStatus():void
		{
			wKey = 0;
			aKey = 0;
			sKey = 0;
			dKey = 0;
			xKey = 0;
			yKey = 0;
		}
		
		/**
		 * 按键按下侦听
		 * 设置不同按键的按下状态
		 */ 
		protected function onKeyDown(e:KeyboardEvent):void
		{
			switch(e.keyCode)
			{
				case 87:
				case 38:
					// W & UP
					wKey = -1;
					yKey = -1;
					break;
				case 65:
				case 37:
					// A & LEFT
					aKey = -1;
					xKey = -1;
					break;
				case 83:
				case 40:
					// S & DOWN
					sKey = 1;
					yKey = 1;
					break;
				case 68:
				case 39:
					// D & RIGHT
					dKey = 1;
					xKey = 1;
					break;
			}
		}
		
		/**
		 * 键盘弹起
		 */ 
		protected function onKeyUp(e:KeyboardEvent):void
		{
			//if(isStatic) return;
			if(_me.action==Actions.Die)
			{
				resetKeyStatus();
				return;
			}
			
			switch(e.keyCode)
			{
				case 87:
				case 38:
					// W & UP
					wKey = 0;
					yKey = 0;
					break;
				case 65:
				case 37:
					// A & LEFT
					aKey = 0;
					xKey = 0;
					break;
				case 83:
				case 40:
					// S & DOWN
					sKey = 0;
					yKey = 0;
					break;
				case 68:
				case 39:
					// D & RIGHT
					dKey = 0;
					xKey = 0;
					break;
			}
			
			/**
			 * 处理相反键触发
			 * 防止同时修改x或y的移动状态
			 */ 
			if(wKey!=0) yKey = wKey;
			if(sKey!=0) yKey = sKey;
			if(aKey!=0) xKey = aKey;
			if(dKey!=0) xKey = dKey;
		}

	}
}